<?php
namespace App\Http\Controllers\Front;

use App\Service\Front\PayService;
use Illuminate\Http\Request;
require_once(__DIR__.'/../../../../vendor/autoload.php');
use WeChatPay\Builder;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Transformer;

class PayWxController extends BaseController
{
    //支付

    /**
     * 必须的参数
     */
    //appid
    protected $app_id = 'wx4171136552ce7ceb';
    //商户号
    protected $mch_id = '1691588547';
    //秘钥2
    protected $key2 = '955bb64e81839629beb44b7ecc155eb4';
    //秘钥3
    protected $key = '955bb64e81839629beb44b7ecc155eb4';
    protected $notify_url = ''; //通知接口
    //微信证书序列号：4A730B76F65D8C40C82BB1DB4B288D07E3B180BE
    protected $merchantCertificateSerial = '4A730B76F65D8C40C82BB1DB4B288D07E3B180BE';
    //平台证书序列号：45D803CC450C6D186427C789D6692665358D8F80
    protected $platformCertificateSerial = '45D803CC450C6D186427C789D6692665358D8F80';
    protected $wxUrl = 'https://api.mch.weixin.qq.com/v3/pay/transactions/native';
    protected $schema = 'WECHATPAY2-SHA256-RSA2048';
    protected $apiclient_key = 'wxkey/apiclient_key.pem';

    public function __construct(PayService $service)
    {
        $this->db = $service;
        $this->notify_url = env('DOMAIN') . '/api/pc/pay/wx/notice';
    }

    public function test(){
        $orderno = (string) toOrderno();
        $body = [
            "appid" => $this->app_id,
            "mchid" => $this->mch_id,
            "description" => "order",
            "out_trade_no" => $orderno,
            "notify_url" => $this->notify_url,
            "amount" => [
                "total" => (int) 1,
            ],
        ];
        $postData = json_encode($body, JSON_UNESCAPED_UNICODE);
        $postData = str_replace("\\", "", $postData);
//        var_dump($postData);  echo'<br>json<br>';
        $authstr = $this->signHeader($postData);
        var_dump($authstr); echo '<br>authstr<br>';
        $headers = [
            "Content-Type:application/json",
            "Accept:application/json",
            "User-Agent:" . $_SERVER['HTTP_USER_AGENT'],
            "Authorization:" . $authstr,
        ];
//        var_dump($postData, $headers);echo "<br>headers<br>";
        $res = $this->toCurl($this->wxUrl, $postData, $headers);
//        $res = json_decode($res, true);
        var_dump($res);
    }

    //获取签名头
    public function signHeader($params = '')
    {
        $nonce = (string) time() . 'SJ' . time();
        $timestamp = (string) time();
        $method = 'POST';
        //$urlParts = parse_url($_SERVER['REQUEST_URI']);
        $urlParts = parse_url($this->wxUrl);
        $wxUrl = ($urlParts['path'] . (!empty($urlParts['query']) ? "?{$urlParts['query']}" : ""));
        $sign_str = strtoupper($method) . "\n" .
            $wxUrl . "\n" .
            $timestamp . "\n" .
            $nonce . "\n" .
            $params . "\n";
        var_dump($sign_str); echo "<br>sign_str<br>";
        if (!in_array('sha256WithRSAEncryption', openssl_get_md_methods(true))) {
            throw new \RuntimeException("当前PHP环境不支持SHA256withRSA");
        }
//        $sign_str = sprintf("%s\n%s\n%s\n%s\n%s\n", $method, $wxUrl, $timestamp, $nonce, $params);
//        var_dump($sign_str); echo "<br>sign_str<br>";
        $prikey = openssl_pkey_get_private(file_get_contents($this->apiclient_key));
        if (!openssl_sign($sign_str, $sign, $prikey, 'sha256WithRSAEncryption')) {
            throw new \UnexpectedValueException("签名验证过程发生了错误");
        }
        $sign = base64_encode($sign);
//        var_dump($sign); echo "<br>sign<br>";
        $token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
            $this->mch_id, $nonce, $timestamp, $this->merchantCertificateSerial, $sign
        );
//        var_dump($token); echo "<br>token<br>";
        return $this->schema . ' ' . $token;
    }

//    //测试支付
//    public function test()
//    {
//        $merchantId = $this->mch_id;
//        $merchantPrivateKeyFilePath = 'wxkey/apiclient_key.pem';
////        $merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
//        $merchantPrivateKeyInstance = file_get_contents($merchantPrivateKeyFilePath);
//        $merchantCertificateSerial = $this->merchantCertificateSerial;
//        $platformCertificateOrPublicKeyFilePath = 'wxkey/apiclient_cert.pem';
////        $platformPublicKeyInstance = Rsa::from($platformCertificateOrPublicKeyFilePath, Rsa::KEY_TYPE_PUBLIC);
//        $platformPublicKeyInstance = file_get_contents($platformCertificateOrPublicKeyFilePath);
//        $platformCertificateSerialOrPublicKeyId = $this->platformCertificateSerial;
//        $instance = Builder::factory([
//            'mchid'      => $merchantId,
//            'serial'     => $merchantCertificateSerial,
//            'privateKey' => $merchantPrivateKeyInstance,
//            'certs'      => [
//                $platformCertificateSerialOrPublicKeyId => $platformPublicKeyInstance,
//            ],
//        ]);
////        $resp = $instance->chain('v3/certificates')->get(
////            /** @see https://docs.guzzlephp.org/en/stable/request-options.html#debug */
////             ['debug' => true] // 调试模式
////        );
////        echo (string) $resp->getBody(), PHP_EOL;
//        try {
//            $resp = $instance
//                ->chain('v3/pay/transactions/native')
//                ->post(['json' => [
//                    'mchid'        => $this->mch_id,
//                    'out_trade_no' => toOrderno(),
//                    'appid'        => $this->app_id,
//                    'description'  => 'Image形象店-深圳腾大-QQ公仔',
//                    'notify_url'   => $this->notify_url,
//                    'amount'       => [
//                        'total'    => 1,
//                    ],
//                ]]);
//            echo $resp->getStatusCode(), PHP_EOL;
//            echo (string) $resp->getBody(), PHP_EOL;
//        } catch (\Exception $e) {
//            // 进行错误处理
//            echo $e->getMessage(), PHP_EOL;
////            if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
////                $r = $e->getResponse();
////                echo $r->getStatusCode() . ' ' . $r->getReasonPhrase(), PHP_EOL;
////                echo (string) $r->getBody(), PHP_EOL, PHP_EOL, PHP_EOL;
////            }
//            echo $e->getTraceAsString(), PHP_EOL;
//        }
//    }

    /**
     * 微信支付：统一下单
     * https://api.mch.weixin.qq.com/pay/unifiedorder
     */
    public function wxPay(Request $request)
    {
        $token = $request->post('token', ''); //登录token
        $type = $request->post('type', 1); //1原创需求订单
        $orderno = $request->post('orderno', 0); //订单号
        $orderOne = $this->db->orderPrice($type, $orderno);
        if (empty($orderOne)) {
            return $this->toJson(-2, '订单错误');
        }
        $uOne = $this->db->userByToken($token);
        if (!$uOne) {
            return $this->toJson(-2, '未登录');
        }
        $uid = $uOne['id'];
        $openid = $uOne['openid'];
        $userIp = $this->getUserIp();
        $nonce = $orderOne['orderno'];
        $amount = $orderOne['price'];
        //构建签名
        $signs = [
            'appid' => $this->app_id,
            'mch_id' => $this->mch_id,
            'nonce_str' => $nonce,
            'body' => 'order',
            'notify_url' => $this->notify_url,
            'out_trade_no' => $orderno,
            'spbill_create_ip' => $userIp,
            'total_fee' => $amount,
            'trade_type' => 'JSAPI',
            'sign_type' => 'MD5',
            'openid' => $openid,
        ];
        ksort($signs);
//        var_dump($signs); echo '<br>1';
        $string = '';
        foreach ($signs as $k => $v) {
            if ($v) {
                $string .= $k . '=' . $v . '&';
            }
        }
        $string .= 'key=' . $this->key;
//        var_dump($string); echo '<br>2';
        $payData = [
            'uid' => $uid, 'from_type' => $type,
            'out_trade_no' => $orderno, 'money' => $amount,
            'order_sign' => $string,
        ];
        $lastid = $this->db->wxPayAdd($payData); //支付log
        $signStr = strtoupper(md5($string));
//        var_dump($signStr); echo '<br>3';
        //构建请求参数
        $param = '';
        $param .= '<xml>';
        $param .= '<appid>' . $this->app_id . '</appid>';
        $param .= '<body>' . $signs['body'] . '</body>';
        $param .= '<mch_id>' . $this->mch_id . '</mch_id>';
        $param .= '<nonce_str>' . $nonce . '</nonce_str>';
        $param .= '<notify_url>' . $this->notify_url . '</notify_url>';
        $param .= '<out_trade_no>' . $orderno . '</out_trade_no>';
        $param .= '<spbill_create_ip>' . $userIp . '</spbill_create_ip>';
        $param .= '<total_fee>' . $amount . '</total_fee>';
        $param .= '<trade_type>' . $signs['trade_type'] . '</trade_type>';
        $param .= '<openid>' . $signs['openid'] . '</openid>';
        $param .= '<sign_type>' . $signs['sign_type'] . '</sign_type>';
        $param .= '<sign>' . $signStr . '</sign>';
        $param .= '</xml>';
//        var_dump($param); echo '<br>4';
        $this->db->wxPayModify($lastid, ['order_req'=>$param]);
        $httpUrl = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
        $res = $this->toCurl($httpUrl, $param, 1);
//        var_dump($res); echo '<br>5';
        $payData = [
            'order_back' => json_encode($res, JSON_UNESCAPED_UNICODE),
        ];
        $this->db->wxPayModify($lastid, $payData); //支付log
        if ($res['code'] < 0) {
            return $this->toJson(-2, $res['msg']);
        }
        //用于唤起的参数
        $resData = [
            'appId' => $this->app_id,
            'timeStamp' => (string) time(),
            'nonceStr' => toOrderno(),
            'package' => 'prepay_id=' . $res['data']['prepay_id'],
            'signType' => 'MD5',
        ];
        ksort($resData);
        $string = '';
        foreach ($resData as $k => $v) {
            if ($v) {
                $string .= $k . '=' . $v . '&';
            }
        }
        $string .= 'key=' . $this->key;
        $resData['mchId'] = $this->mch_id;
        $resData['orderInfo'] = '案例需求订单';
        $resData['key'] = $this->key;
        $resData['paySign'] = strtoupper(md5($string));
        $payData = [
            'pay_info' => json_encode($resData, JSON_UNESCAPED_UNICODE),
            'status' => 1,
        ];
        $this->db->wxPayModify($lastid, $payData); //支付log
        return $this->toJson(0, '', $resData);
    }

    //微信通知
    public function notice()
    {
        $xml = file_get_contents('php://input');
        if (!$xml) {
            return $this->toJson();
        }
        $lastid = $this->db->wxNotice($xml);
        $prefix = '<![CDATA['; $suffix = ']]>';
        //return_code
        $res = preg_match('/<return_code>(.*?)<\/return_code>/', $xml, $matches);
        if (!$res) {
            return $this->toJson(-2, 'return_code 缺失');
        }
        $return_code = $matches[1];
        if (substr($return_code, 0, strlen($prefix)) == $prefix) {
            $return_code = substr($return_code, 9);
        }
        if (substr($return_code, -3) == $suffix) {
            $return_code = substr($return_code, 0, -3);
        }
        if ($return_code != 'SUCCESS') {
            return $this->toJson(-2, '通信失败');
        }
        $items['return_code'] = $return_code;
        //result_code
        $res = preg_match('/<result_code>(.*?)<\/result_code>/', $xml, $matches);
        if (!$res) {
            return $this->toJson(-2, 'result_code 缺失');
        }
        $result_code = $matches[1];
        if (substr($result_code, 0, strlen($prefix)) == $prefix) {
            $result_code = substr($result_code, 9);
        }
        if (substr($result_code, -3) == $suffix) {
            $result_code = substr($result_code, 0, -3);
        }
        if ($result_code != 'SUCCESS') {
            return $this->toJson(-2, '交易失败');
        }
        $items['result_code'] = $return_code;
        //out_trade_no
        $res = preg_match('/<out_trade_no>(.*?)<\/out_trade_no>/', $xml, $matches);
        if (!$res) {
            return $this->toJson(-2, 'out_trade_no 缺失');
        }
        $out_trade_no = $matches[1];
        if (substr($out_trade_no, 0, strlen($prefix)) == $prefix) {
            $out_trade_no = substr($out_trade_no, 9);
        }
        if (substr($out_trade_no, -3) == $suffix) {
            $out_trade_no = substr($out_trade_no, 0, -3);
        }
        $items['out_trade_no'] = $out_trade_no;
        $this->db->wxNoticeModify($lastid, $out_trade_no);
        return $this->toJson(0, '', $items);
    }

    /**
     * 构建CURL请求，单线程
     * @param string $httpUrl 请求地址
     * @param string $string 参数拼接的字符串
     * @return $json
     */
    public function toCurl($httpUrl = '', $string = '', $headers = [])
    {
//        var_dump($string); echo '<br>curl<br>';
        $curl = curl_init(); //初始化
        curl_setopt($curl,CURLOPT_URL,$httpUrl);
        curl_setopt($curl, CURLOPT_HEADER, 0);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
        curl_setopt($curl, CURLOPT_FORBID_REUSE, 1);
        curl_setopt($curl, CURLOPT_FRESH_CONNECT, 1);
        curl_setopt($curl, CURLOPT_ENCODING, 'UTF-8');
        curl_setopt($curl, CURLOPT_POST, 1);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $string);
        curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($curl, CURLOPT_TIMEOUT, 5);
        $output = curl_exec($curl);
        curl_close($curl);
        return $output;
    }

    //解析xml
    public function xml2arr($xml)
    {
        $items = [];
        ////////////////////////正则匹配
        $prefix = '<![CDATA['; $suffix = ']]>';
        //return_code
        $res = preg_match('/<return_code>(.*?)<\/return_code>/', $xml, $matches);
        if (!$res) {
            return array('code' => -2, 'msg' => 'return_code 缺失', 'data' => $xml);
        }
        $return_code = $matches[1];
        if (substr($return_code, 0, strlen($prefix)) == $prefix) {
            $return_code = substr($return_code, 9);
        }
        if (substr($return_code, -3) == $suffix) {
            $return_code = substr($return_code, 0, -3);
        }
        $items['return_code'] = $return_code;
        //result_code
        $res = preg_match('/<result_code>(.*?)<\/result_code>/', $xml, $matches);
        if (!$res) {
            return array('code' => -2, 'msg' => 'result_code 缺失', 'data' => $xml);
        }
        $result_code = $matches[1];
        if (substr($result_code, 0, strlen($prefix)) == $prefix) {
            $result_code = substr($result_code, 9);
        }
        if (substr($result_code, -3) == $suffix) {
            $result_code = substr($result_code, 0, -3);
        }
        $items['result_code'] = $result_code;
        //prepay_id
        $res = preg_match('/<prepay_id>(.*?)<\/prepay_id>/', $xml, $matches);
        if (!$res) {
            return array('code' => -2, 'msg' => 'prepay_id 缺失', 'data' => $xml);
        }
        $prepay_id = $matches[1];
        if (substr($prepay_id, 0, strlen($prefix)) == $prefix) {
            $prepay_id = substr($prepay_id, 9);
        }
        if (substr($prepay_id, -3) == $suffix) {
            $prepay_id = substr($prepay_id, 0, -3);
        }
        $items['prepay_id'] = $prepay_id;
        //return_msg
        $res = preg_match('/<return_msg>(.*?)<\/return_msg>/', $xml, $matches);
        if (!$res) {
            return array('code' => -2, 'msg' => 'prepay_id 缺失', 'data' => $xml);
        }
        $return_msg = $matches[1];
        if (substr($return_msg, 0, strlen($prefix)) == $prefix) {
            $return_msg = substr($return_msg, 9);
        }
        if (substr($return_msg, -3) == $suffix) {
            $return_msg = substr($return_msg, 0, -3);
        }
        $items['return_msg'] = $return_msg;
        //判断错误
        if ($return_code != 'SUCCESS' || $result_code != 'SUCCESS') {
            return array('code' => -2, 'msg' => $return_msg, 'data' => $xml);
        }
        return array('code' => 0, 'msg' => 'success', 'data' => $items);
    }

    //数组转xml
    public function arr2Xml($arr)
    {
        $xml = "<xml>";
        foreach ($arr as $key => $val) {
            if (is_numeric($val)) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
        }
        $xml .= "</xml>";
        return $xml;
    }

    /**
     * 获取签名
     */
    public function wxSign($params, $key)
    {
        ksort($params, SORT_STRING);
        $unSignParaString = $this->formatQueryParaMap($params);
        $signStr = strtoupper(md5($unSignParaString . "&key=" . $key));
        return $signStr;
    }

    public function formatQueryParaMap($paraMap)
    {
        $buff = "";
        ksort($paraMap);
        foreach ($paraMap as $k => $v) {
            if ($v) {
                $buff .= $k . "=" . $v . "&";
            }
        }
        $reqPar = '';
        if (strlen($buff) > 0) {
            $reqPar = substr($buff, 0, strlen($buff) - 1);
        }
        return $reqPar;
    }
}